namespace C4F.DevKit.Bluetooth
{
    namespace ServiceAndDeviceDiscovery
    {
        #region Using Directives
        using System;
        using System.Collections.Generic;
        using System.Text;
        using System.Runtime.InteropServices;
        using Resources;
        using System.ComponentModel;
        #endregion

        /// <summary>
        /// Helper class with static methods which aids the client 
        /// application in device and service discovery
        /// </summary>
        public class BluetoothDeviceServicesManager : Component
        {

            #region Public Functions
            /// <summary>
            /// Discover all devices.
            /// </summary>
            /// <returns>List of Discovered devices.</returns>
            /// <exception cref="WinsockException">Thrown when 
            /// when a windows socket error occurs.</exception> 
            public List<Device> DiscoverAllDevices()
            {
                List<Device> devices = new List<Device>();

                // Initialize WinSock
                WsaData wsadata = new WsaData();

                int result =
                    BluetoothHelper.WSAStartup(BluetoothHelper.MakeWord(2, 2),
                                               ref wsadata);
                if (result != 0)
                    BluetoothHelper.GetError();

                // Scan for bluetooth devices
                QuerySet wsaq = new QuerySet();
                //Initialize queryset structure with device specific 
                //information.
                wsaq.Size = Marshal.SizeOf(typeof(QuerySet));
                wsaq.NameSpace = BluetoothHelper.NS_BTH;
                IntPtr lookup = IntPtr.Zero;
                uint flags = BluetoothHelper.LUP_RETURN_NAME
                               | BluetoothHelper.LUP_CONTAINERS
                               | BluetoothHelper.LUP_RETURN_ADDR
                               | BluetoothHelper.LUP_FLUSHCACHE
                               | BluetoothHelper.LUP_RETURN_TYPE
                               | BluetoothHelper.LUP_RETURN_BLOB
                               | BluetoothHelper.LUP_RES_SERVICE;

                //Initiates a client query that is constrained by the 
                //information contained within a queryset structure.

                result = BluetoothHelper.WSALookupServiceBegin(wsaq,
                                                               flags,
                                                               ref lookup);
                if (result != 0)
                    BluetoothHelper.GetError();

                while (0 == result)
                {
                    int buffer = 0x10000;

                    IntPtr bufferPtr = Marshal.AllocHGlobal(buffer);
                    QuerySet qsResult = new QuerySet();

                    //Retrieves the requested device information.
                    result = BluetoothHelper.WSALookupServiceNext(lookup,
                                                             flags,
                                                             ref buffer,
                                                             bufferPtr);
                    if (0 == result)
                    {
                        Marshal.PtrToStructure(bufferPtr, qsResult);
                        devices.Add(new Device(qsResult));
                    }
                    else
                    {
                        BluetoothHelper.GetError();
                    }
                }
                //end device-lookup
                result = BluetoothHelper.WSALookupServiceEnd(lookup);
                if (result != 0)
                    BluetoothHelper.GetError();

                // cleanup winsock
                result = BluetoothHelper.WSACleanup();

                if (result != 0)
                    BluetoothHelper.GetError();

                return devices;
            }

            /// <summary>
            /// Discovers devices by specified device type.
            /// </summary>
            /// <param name="type">Represents the type of device to be 
            /// discovered.</param>
            /// <returns>List of devices whose type matches that of the 
            /// specified type.</returns>
            /// <exception cref="WinsockException">Thrown when 
            /// when a windows socket error occurs.</exception>
            /// <exception cref="ArgumentNullException">Thrown when 
            /// when a null reference (not set or empty) is passed 
            /// to a method that does not accept it as a valid argument.
            /// </exception> 
            public List<Device> DiscoverDeviceByType(DeviceType type)
            {
                if (!Enum.IsDefined(typeof(DeviceType), type))
                    throw new ArgumentException(SDPMessages.ErrorMsgDeviceType);

                List<Device> devices = new List<Device>();
                List<Device> devicesList = new List<Device>();
                //Discover all devices
                devices = DiscoverAllDevices();

                foreach (Device dev in devices)
                {
                    if (dev.Type == type)
                        devicesList.Add(dev);
                }
                return devicesList;
            }

            /// <summary>
            /// Discover device by specified device name.
            /// </summary>
            /// <param name="name">Represents the name of the device to be
            /// discovered.</param>
            /// <returns>Device whose name matches that of the specified name.
            /// </returns>
            /// <exception cref="WinsockException">Thrown when 
            /// when a windows socket error occurs.</exception>
            /// <exception cref="ArgumentNullException">Thrown when 
            /// when a null reference (not set or empty) is passed 
            /// to a method that does not accept it as a valid argument.
            /// </exception> 
            public Device DiscoverDeviceByName(string name)
            {
                if (string.IsNullOrEmpty(name))
                    throw new ArgumentNullException
                            (SDPMessages.ErrorMsgNullDeviceName);

                Device device = null;
                List<Device> devices = new List<Device>();
                devices = DiscoverAllDevices();

                foreach (Device dev in devices)
                {
                    if (dev.Name == name)
                    {
                        device = dev;
                        break;
                    }
                }
                return device;
            }
            /// <summary>
            /// Discover all services on device.
            /// </summary>
            /// <param name="device">Represents the device whose services have 
            /// to be discovered.</param>
            /// <returns>List of discovered services.</returns>
            /// <exception cref="WinsockException">Thrown when 
            /// when a windows socket error occurs.</exception>
            /// <exception cref="ArgumentNullException">Thrown when 
            /// when a null reference (not set or empty) is passed 
            /// to a method that does not accept it as a valid argument.
            /// </exception> 
            public  List<RemoteService> DiscoverAllServices(Device device)
            {
                if (device == null)
                    throw new ArgumentNullException
                                (SDPMessages.ErrorMsgNullDevice);

                return device.Services;
            }
            /// <summary>
            /// Discover Service by service id which is specified in Guid format.
            /// </summary>
            /// <param name="device">Represents the device whose services have 
            /// to be discovered.</param>
            /// <param name="id">Represents the id of the service to be 
            /// discovered.</param>
            /// <returns>Returns the remote service instance whose id matched 
            /// that of the specified id</returns>
            /// <exception cref="WinsockException">Thrown when 
            /// when a windows socket error occurs.</exception>
            /// <exception cref="ArgumentNullException">Thrown when 
            /// when a null reference (not set or empty) is passed 
            /// to a method that does not accept it as a valid argument.
            /// </exception> 
            public RemoteService DiscoverServiceById(Device device,
                                                            Guid id)
            {
                if (device == null)
                    throw new ArgumentNullException
                        (SDPMessages.ErrorMsgNullDevice);


                RemoteService remoteService
                    = device.DiscoverServiceById(id);

                return remoteService;
            }
            /// <summary>
            /// Discover Service by service id which is specified in ushort format.
            /// </summary>
            /// <param name="device">Represents the device whose services have 
            /// to be discovered.</param>
            /// <param name="id">Represents the id of the service to be 
            /// discovered.</param>
            /// <returns>Returns the remote service instance whose id matched 
            /// that of the specified id</returns>
            /// <exception cref="WinsockException">Thrown when 
            /// when a windows socket error occurs.</exception>
            /// <exception cref="ArgumentNullException">Thrown when 
            /// when a null reference (not set or empty) is passed 
            /// to a method that does not accept it as a valid argument.
            /// </exception> 
            public RemoteService DiscoverServiceById(Device device,
                                                            ushort id)
            {
                if (device == null)
                    throw new ArgumentNullException
                            (SDPMessages.ErrorMsgNullDevice);


                RemoteService remoteService = device.DiscoverServiceById(id);
                return remoteService;

            }
            /// <summary>
            /// Discover Service by service id which is specified in uint format.
            /// </summary>
            /// <param name="device">Represents the device whose services have 
            /// to be discovered.</param>
            /// <param name="id">Represents the id of the service to be 
            /// discovered.</param>
            /// <returns>Returns the remote service instance whose id matched 
            /// that of the specified id</returns>
            /// <exception cref="WinsockException">Thrown when 
            /// when a windows socket error occurs.</exception>
            /// <exception cref="ArgumentNullException">Thrown when 
            /// when a null reference (not set or empty) is passed 
            /// to a method that does not accept it as a valid argument.
            /// </exception> 
            public RemoteService DiscoverServiceById(Device device,
                                                            uint id)
            {
                if (device == null)
                    throw new ArgumentNullException(
                       SDPMessages.ErrorMsgNullDevice);

                RemoteService remoteService =
                    device.DiscoverServiceById(id);

                return remoteService;
            }
            /// <summary>
            /// Discover Services by service type. 
            /// </summary>
            /// <param name="device">Represents the device whose services have 
            /// to be discovered.</param>
            /// <param name="type">Represents the type of the service to be 
            /// discovered.</param>
            /// <returns>Returns a list of remote service instances whose type matches 
            /// that of the specified type</returns>
            /// <exception cref="WinsockException">Thrown when 
            /// when a windows socket error occurs.</exception>
            /// <exception cref="ArgumentNullException">Thrown when 
            /// when a null reference (not set or empty) is passed 
            /// to a method that does not accept it as a valid argument.
            /// </exception> 
            public List<RemoteService> DiscoverServicesByType(Device device,
                                                                  ServiceType type)
            {
                if (device == null)
                    throw new ArgumentNullException(
                       SDPMessages.ErrorMsgNullDevice);

                if (type == null)
                    throw new ArgumentNullException(
                       SDPMessages.ErrorMsgNullServiceType);

                List<RemoteService> remoteServices = device.DiscoverServicesByType(type);
                return remoteServices;
            }
            #endregion
        }
    }
}